import Database, { Database as SqliteDatabase } from 'better-sqlite3';
import SessionsTable from '../models/SessionsTable';

interface IDbOptions {
  readonly?: boolean;
  fileMustExist?: boolean;
}

export default class SessionsDb {
  private static dbByBaseDir: { [dir: string]: SessionsDb } = {};
  public readonly sessions: SessionsTable;
  public readonly readonly: boolean;
  private readonly baseDir: string;
  private db: SqliteDatabase;

  constructor(baseDir: string, dbOptions: IDbOptions = {}) {
    const { readonly = false, fileMustExist = false } = dbOptions;
    this.db = new Database(`${baseDir}/sessions.db`, { readonly, fileMustExist });
    this.baseDir = baseDir;
    this.readonly = readonly;
    this.sessions = new SessionsTable(this.db);
  }

  public findLatestSessionId(script: {
    sessionName: string;
    scriptInstanceId: string;
    scriptEntrypoint?: string;
  }) {
    const { sessionName, scriptEntrypoint, scriptInstanceId } = script;
    if (sessionName && scriptInstanceId) {
      const sessionRecord = this.sessions.findByName(sessionName, scriptInstanceId);
      return sessionRecord.id;
    }
    if (scriptEntrypoint) {
      const sessionRecords = this.sessions.findByScriptEntrypoint(scriptEntrypoint);
      return sessionRecords[0].id;
    }
  }

  public findRelatedSessions(session: { scriptEntrypoint: string; scriptInstanceId: string }) {
    const otherSessions = this.sessions.findByScriptEntrypoint(session.scriptEntrypoint);
    const relatedScriptInstances: {
      id: string;
      startDate: string;
      defaultSessionId: string;
    }[] = [];
    const relatedSessions: { id: string; name: string }[] = [];
    for (const otherSession of otherSessions) {
      relatedScriptInstances.push({
        id: otherSession.scriptInstanceId,
        startDate: otherSession.scriptStartDate,
        defaultSessionId: otherSession.id,
      });
      if (otherSession.scriptInstanceId === session.scriptInstanceId) {
        relatedSessions.push({ id: otherSession.id, name: otherSession.name });
      }
    }
    return {
      relatedSessions,
      relatedScriptInstances,
    };
  }

  public close() {
    if (this.db) {
      this.db.close();
    }
    this.db = null;
    delete SessionsDb.dbByBaseDir[this.baseDir];
  }

  public static shutdown() {
    for (const [key, db] of Object.entries(SessionsDb.dbByBaseDir)) {
      db.close();
      delete SessionsDb.dbByBaseDir[key];
    }
  }

  public static find(baseDir: string) {
    if (!this.dbByBaseDir[baseDir]) {
      this.dbByBaseDir[baseDir] = new SessionsDb(baseDir);
    }
    return this.dbByBaseDir[baseDir];
  }
}
